package com.flask.colorpicker.cameralibrary;

import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_PICTURE;

import com.flask.colorpicker.cameralibrary.constant.Constants;
import com.flask.colorpicker.cameralibrary.listener.CameraStateCallbackImpl;
import com.flask.colorpicker.cameralibrary.listener.ErrorListener;
import com.flask.colorpicker.cameralibrary.state.CameraMachine;

import com.flask.colorpicker.cameralibrary.util.LogUtil;
import com.flask.colorpicker.cameralibrary.util.AngleUtil;

import ohos.agp.animation.AnimatorGroup;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.components.Image;
import ohos.agp.graphics.SurfaceOps;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.camera.CameraKit;

import ohos.media.camera.device.FrameConfig;
import ohos.media.camera.device.Camera;
import ohos.media.camera.device.CameraInfo;

import ohos.media.image.ImageReceiver;
import ohos.sensor.agent.CategoryMotionAgent;
import ohos.sensor.bean.CategoryMotion;
import ohos.sensor.data.CategoryMotionData;
import ohos.sensor.listener.ICategoryMotionDataCallback;

import java.io.File;

/**
 * Camera Interface
 */
public class CameraInterface {
    /**
     * TYPE RECORDER
     */
    public static final int TYPE_RECORDER = 0x090;

    /**
     * TYPE RECORDER
     */
    public static final int TYPE_CAPTURE = 0x091;

    private static final String TAG = "CameraView";

    private static final long INTERVAL = 100000000;

    private static volatile CameraInterface sCameraInterface;

    private final EventHandler mCameraHandler = new EventHandler(EventRunner.create("CameraThread"));

    private CameraKit mCameraKit;
    private CameraStateCallbackImpl mCameraStateCallback;

    private FrameConfig.Builder mParams;

    private int SELECTED_CAMERA;
    private int CAMERA_BACK_POSITION = 0;
    private int CAMERA_FRONT_POSITION = 1;
    private boolean isPreviewing = false;
    private float mScreenProp = -1.0f;

    private ErrorListener mErrorListener;

    private Image mSwitchView;
    private Image mFlashLamp;

    private int mAngle = 0;
    private int mRotation = 0;

    private int mNowScaleRate = 0;

    private CategoryMotionAgent mSm = null;

    /**
     * 获取CameraInterface单例
     *
     * @return CameraInterface
     */
    public static synchronized CameraInterface getInstance() {
        if (sCameraInterface == null) {
            synchronized (CameraInterface.class) {
                if (sCameraInterface == null) {
                    sCameraInterface = new CameraInterface();
                }
            }
        }
        return sCameraInterface;
    }

    private final ICategoryMotionDataCallback sensorEventListener =
            new ICategoryMotionDataCallback() {
                @Override
                public void onSensorDataModified(CategoryMotionData event) {
                    if (event.sensor.getFlags() != CategoryMotion.SENSOR_TYPE_ACCELEROMETER) {
                        return;
                    }
                    float[] values = event.values;
                    mAngle = AngleUtil.getSensorAngle(values[0], values[1]);
                    rotationAnimation();
                }

                @Override
                public void onAccuracyDataModified(CategoryMotion categoryMotion, int i) {
                }

                @Override
                public void onCommandCompleted(CategoryMotion categoryMotion) {
                }
            };

    /**
     * 切换摄像头icon跟随手机角度进行旋转
     */
    private void rotationAnimation() {
        if (mRotation != mAngle) {
            int[] rotation = getRotation();
            startRotation(rotation[0], rotation[1]);
            mRotation = mAngle;
        }
    }

    private int[] getRotation() {
        int[] rotations = new int[2];
        switch (mRotation) {
            case 0:
                switch (mAngle) {
                    case 90:
                        rotations[1] = -90;
                        break;
                    case 270:
                        rotations[1] = 90;
                        break;
                }
                break;
            case 90:
                rotations[0] = -90;
                switch (mAngle) {
                    case 0:
                        break;
                    case 180:
                        rotations[1] = -180;
                        break;
                }
                break;
            case 180:
                rotations[0] = 180;
                switch (mAngle) {
                    case 90:
                        rotations[1] = 270;
                        break;
                    case 270:
                        rotations[1] = 90;
                        break;
                }
                break;
            case 270:
                rotations[0] = 90;
                switch (mAngle) {
                    case 0:
                        break;
                    case 180:
                        rotations[1] = 180;
                        break;
                }
                break;
        }
        return rotations;
    }

    private void startRotation(int startRotation, int endRotation) {
        if (mSwitchView == null) {
            return;
        }
        AnimatorProperty switchAnimation = new AnimatorProperty(mSwitchView);
        switchAnimation.rotate(startRotation).rotate(endRotation);

        AnimatorProperty flashAnimation = new AnimatorProperty(mFlashLamp);
        flashAnimation.rotate(startRotation).rotate(endRotation);

        AnimatorGroup animatorGroup = new AnimatorGroup();
        animatorGroup.runParallel(switchAnimation, flashAnimation);
        animatorGroup.setDuration(500);
        animatorGroup.start();
    }

    void setSaveVideoPath(String saveVideoPath) {
        File file = new File(saveVideoPath);
        if (!file.exists()) {
            boolean isSuccess = file.mkdirs();
            if (!isSuccess) {
                LogUtil.error(LogUtil.DEFAULT_TAG, "file create fail");
            }
        }
    }

    /**
     * set Zoom
     *
     * @param zoom float
     * @param type int
     */
    public void setZoom(float zoom, int type) {
        LogUtil.error(LogUtil.DEFAULT_TAG, "set zoom:" + zoom + ";type:" + type);
        if (mCameraStateCallback == null || mCameraStateCallback.getCamera() == null) {
            return;
        }
        if (mParams == null) {
            mParams =
                    mCameraStateCallback.getCamera().getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
        }
        switch (type) {
            case TYPE_RECORDER:
                // 如果不是录制视频中，上滑不会缩放
                return;
            case TYPE_CAPTURE:
                // 每移动50个像素缩放一个级别
                int scaleRate = (int) (zoom / 50);
                if (scaleRate < mParams.getZoomValue()) {
                    mNowScaleRate += scaleRate;
                    if (mNowScaleRate < 0) {
                        mNowScaleRate = 0;
                    } else if (mNowScaleRate > mParams.getZoomValue()) {
                        mNowScaleRate = (int) mParams.getZoomValue();
                    } else {
                        LogUtil.info(TAG, "mNowScaleRate = " + mNowScaleRate);
                    }
                    setZoom(mNowScaleRate);
                }
                LogUtil.info(TAG, "setZoom = " + mNowScaleRate);
                break;
        }
    }

    private void setZoom(float zoom) {
        LogUtil.error(LogUtil.DEFAULT_TAG, "set zoom:" + zoom);
        if (mCameraStateCallback == null || mCameraStateCallback.getCamera() == null) {
            return;
        }
        FrameConfig.Builder builder =
                mCameraStateCallback.getCamera().getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
        builder.setZoom(zoom);
        mCameraStateCallback.getCamera().triggerLoopingCapture(builder.build());
    }

    /**
     * set FlashMode
     *
     * @param flashMode int
     */
    public void setFlashMode(int flashMode) {
        if (mCameraStateCallback == null || mCameraStateCallback.getCamera() == null) {
            return;
        }
        FrameConfig.Builder builder =
                mCameraStateCallback.getCamera().getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
        builder.addSurface(
                mCameraStateCallback.getMachine().getView().getSurfaceProvider().getSurfaceOps().get().getSurface());
        builder.setFlashMode(flashMode);
        mCameraStateCallback.getCamera().triggerLoopingCapture(builder.build());
    }

    /**
     * Camera OpenOver Callback
     */
    public interface CameraOpenOverCallback {
        void cameraHasOpened();
    }

    private CameraInterface() {
        findAvailableCameras();
//        SELECTED_CAMERA = CAMERA_BACK_POSITION;
        setSELECTED_CAMERA(CAMERA_BACK_POSITION);
    }

    public int getSELECTED_CAMERA() {
        return SELECTED_CAMERA;
    }

    public void setSELECTED_CAMERA(int SELECTED_CAMERA) {
        this.SELECTED_CAMERA = SELECTED_CAMERA;
    }

    public int getCAMERA_FRONT_POSITION() {
        return CAMERA_FRONT_POSITION;
    }

    public void setCAMERA_FRONT_POSITION(int CAMERA_FRONT_POSITION) {
        this.CAMERA_FRONT_POSITION = CAMERA_FRONT_POSITION;
    }

    /**
     * re Open Camera
     *
     * @param machine CameraMachine
     */
    public void reOpenCamera(CameraMachine machine) {
        if (mCameraStateCallback != null && mCameraStateCallback.getCamera() != null) {
            mCameraStateCallback.getCamera().release();
        }
        mCameraStateCallback = null;
        openCamera(machine);
    }

    /**
     * open Camera
     *
     * @param callback CameraOpenOverCallback
     * @param machine  CameraMachine
     */
    void doOpenCamera(CameraOpenOverCallback callback, CameraMachine machine) {
        if (mCameraStateCallback == null || mCameraStateCallback.getCamera() == null) {
            openCamera(machine);
        }
        callback.cameraHasOpened();
    }

    private void openCamera(CameraMachine machine) {
        LogUtil.error(LogUtil.DEFAULT_TAG, "openCamera");
        if (mCameraStateCallback == null) {
            mCameraStateCallback = new CameraStateCallbackImpl(machine);
        }
        try {
            mCameraKit = CameraKit.getInstance(machine.getContext().getApplicationContext());
            if (mCameraKit == null) {
                if (mErrorListener != null) {
                    mErrorListener.onError("CameraKit Invalid", Constants.ERROR_TYPE_PICTURE);
                }
                LogUtil.error(TAG, "there is no camera");
                return;
            }
            changeCamera(SELECTED_CAMERA);

        } catch (Exception var3) {
            if (this.mErrorListener != null) {
                this.mErrorListener.onError("openCamera exception:" + var3.getMessage(), Constants.ERROR_TYPE_PICTURE);
            }
        }
        LogUtil.error(LogUtil.DEFAULT_TAG, "openCamera end");
    }

    /**
     * 相机前后切换
     *
     * @param cameraTag int
     */
    private void changeCamera(int cameraTag) {
        String[] cameraIds = mCameraKit.getCameraIds();
        if (cameraIds == null || cameraIds.length <= 0) {
            if (mErrorListener != null) {
                mErrorListener.onError("getCameraIds Invalid", Constants.ERROR_TYPE_PICTURE);
            }
            LogUtil.error(TAG, "cameraIds size is 0");
            return;
        }
        boolean isBack = cameraTag == CAMERA_BACK_POSITION;
        String mCameraId = cameraIds.length > 1 && isBack ? cameraIds[0] : cameraIds[1];
        mCameraKit.createCamera(mCameraId, mCameraStateCallback, mCameraHandler);
        LogUtil.error(LogUtil.DEFAULT_TAG, "open camera success");
    }

    /**
     * switch Camera
     *
     * @param holder     SurfaceOps
     * @param screenProp screenProp
     * @param machine    CameraMachine
     */
    public synchronized void switchCamera(SurfaceOps holder, float screenProp, CameraMachine machine) {
        if (SELECTED_CAMERA == CAMERA_BACK_POSITION) {
//            SELECTED_CAMERA = CAMERA_FRONT_POSITION;
            setSELECTED_CAMERA(CAMERA_FRONT_POSITION);
        } else {
//            SELECTED_CAMERA = CAMERA_BACK_POSITION;
            setSELECTED_CAMERA(CAMERA_BACK_POSITION);
        }
        doDestroyCamera();
        openCamera(machine);
        doStartPreview(holder, screenProp);
    }

    /**
     * doStartPreview
     *
     * @param holder     SurfaceOps
     * @param screenProp float
     */
    public void doStartPreview(SurfaceOps holder, float screenProp) {
        isPreviewing = true;
        System.out.println(holder);
        if (this.mScreenProp < 0) {
            this.mScreenProp = screenProp;
        }
    }

    /**
     * 停止预览
     */
    public void doStopPreview() {
        if (mCameraStateCallback != null && mCameraStateCallback.getCamera() != null) {
            mCameraStateCallback.getCamera().stopLoopingCapture();
            isPreviewing = false;
            LogUtil.info(TAG, "=== Stop Preview ===");
        }
    }

    /**
     * 销毁Camera
     */
    void doDestroyCamera() {
        mErrorListener = null;
        if (mCameraStateCallback != null && mCameraStateCallback.getCamera() != null) {
            mSwitchView = null;
            mFlashLamp = null;
            mCameraStateCallback.getCamera().stopLoopingCapture();
            isPreviewing = false;
            mCameraStateCallback.getCamera().release();
            mCameraStateCallback = null;
            LogUtil.info(TAG, "=== Destroy Camera ===");
        } else {
            LogUtil.info(TAG, "=== Camera  Null===");
        }
    }

    /**
     * 拍照
     */
    public void takePicture() {
        LogUtil.info(LogUtil.DEFAULT_TAG, "takePicture");
        if (mCameraStateCallback == null) {
            return;
        }
        Camera camera = mCameraStateCallback.getCamera();

        ImageReceiver imageReceiver = mCameraStateCallback.getMachine().getView().getImageReceiver();
        FrameConfig.Builder framePictureConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_PICTURE);

        framePictureConfigBuilder.addSurface(imageReceiver.getRecevingSurface());
        framePictureConfigBuilder.setImageRotation(90);
        FrameConfig pictureFrameConfig = framePictureConfigBuilder.build();
        camera.triggerSingleCapture(pictureFrameConfig);
        LogUtil.info(LogUtil.DEFAULT_TAG, "takePicture end");
    }

    private void findAvailableCameras() {
        if (mCameraKit == null) {
            return;
        }
        String[] cameraIds = mCameraKit.getCameraIds();
        if (cameraIds == null || cameraIds.length == 0) {
            return;
        }
        for (String cameraId : cameraIds) {
            CameraInfo info = mCameraKit.getCameraInfo(cameraId);
            switch (info.getFacingType()) {
                case CameraInfo.FacingType.CAMERA_FACING_FRONT:
//                    CAMERA_FRONT_POSITION = info.getFacingType();
                    setCAMERA_FRONT_POSITION(info.getFacingType());
                    break;
                case CameraInfo.FacingType.CAMERA_FACING_BACK:
                    CAMERA_BACK_POSITION = info.getFacingType();
                    break;
                default:
                    break;
            }
        }
    }

    void setErrorListener(ErrorListener errorListener) {
        this.mErrorListener = errorListener;
    }

//    /**
//     * Focus Callback
//     */
//    public interface FocusCallback {
//        void focusSuccess();
//    }

    void registerSensorManager() {
        if (mSm == null) {
            mSm = new CategoryMotionAgent();
        }
        CategoryMotion categoryMotion = mSm.getSingleSensor(CategoryMotion.SENSOR_TYPE_ACCELEROMETER);
        mSm.setSensorDataCallback(sensorEventListener, categoryMotion, INTERVAL);
    }

    void unregisterSensorManager() {
        if (mSm != null) {
            mSm.releaseSensorDataCallback(sensorEventListener);
        }
    }

    void isPreview(boolean res) {
        System.out.println(isPreviewing);
        this.isPreviewing = res;
    }
}
